use std::{collections::VecDeque, io::{self, Write}};

use simple_xlsx_writer::{row, Row, WorkBook};

use crate::{
    testcase::TestCase,
    xmind::{Node, Sheet, Xmind},
};

pub fn load_xmind(path: &std::path::Path) -> io::Result<Xmind> {
    let file = std::fs::File::open(path)?;

    let mut zip =
        zip::ZipArchive::new(file).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

    let content = zip
        .by_name("content.json")
        .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

    let xmind: Xmind =
        serde_json::from_reader(content).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

    Ok(xmind)
}

pub fn sheet_to_test_cases(sheet: Sheet) -> Vec<TestCase> {
    let mut test_cases = Vec::with_capacity(128);
    let mut left_stack_node = Vec::new();
    let mut right_stack_children = Vec::new();

    left_stack_node.push(&sheet.root_topic);
    right_stack_children.push(sheet.root_topic.get_children());

    while !right_stack_children.is_empty() {
        let mut chilren = right_stack_children.pop().unwrap();

        if chilren.is_empty() {
            // 当子节点为空, 表示已进入尾节点, 生成用例
            let expect = left_stack_node.pop().unwrap().title.as_str();
            if let Some(test_case) = stack_to_test_case(&left_stack_node, expect) {
                test_cases.push(test_case);
            }

            while let Some(nodes) = right_stack_children.pop() {
                if nodes.is_empty() {
                    left_stack_node.pop();
                } else {
                    right_stack_children.push(nodes);
                    break;
                }
            }
        } else {
            // 当子节点不为空, 继续插入
            let node = chilren.pop().unwrap();
            right_stack_children.push(chilren);

            left_stack_node.push(node);
            right_stack_children.push(node.get_children());
        }
    }

    test_cases
}

pub fn stack_to_test_case(stack: &[&Node], expect: &str) -> Option<TestCase> {
    // 至少包含"project + module + function"
    // "+" 后的单节点, 表示测试场景
    // "+" 后的双节点, 表示测试分支
    if stack.len() < 3 {
        return None;
    }

    let mut iter = stack.split(|&n| n.title == "+");

    let first_parts = iter.next()?;
    if first_parts.len() < 3 {
        return None;
    }

    // 项目
    let project = first_parts[0];
    // 模块
    let module = first_parts[1];
    // 多级模块
    let full_module_name = first_parts[1..(first_parts.len() - 1)]
        .iter()
        .map(|&n| n.title.as_str())
        .collect::<Vec<_>>()
        .join("-");
    // 功能点
    let function = first_parts[first_parts.len() - 1];

    // 用例名称
    let mut test_case_name = String::new();
    test_case_name.push_str(&full_module_name);
    test_case_name.push_str("-");
    test_case_name.push_str(&function.title);

    // 测试步骤
    let mut test_case_steps = Vec::new();
    let mut step_no = 1;
    if let Some(ref notes) = function.notes {
        let note = notes.plain.content.replace("\n", " ");
        let step = format!("{}.{}", step_no, note);
        test_case_steps.push(step);
        step_no += 1;
    }

    // 测试场景及分支
    while let Some(nodes) = iter.next() {
        if nodes.len() == 1 {
            test_case_name.push_str(", ");
            test_case_name.push_str(&nodes[0].title);

            if let Some(ref notes) = nodes[0].notes {
                let note = notes.plain.content.replace("\n", " ");
                let step = format!("{}.{}", step_no, note);
                test_case_steps.push(step);
                step_no += 1;
            }
        } else if nodes.len() == 2 {
            test_case_name.push_str(", ");
            let condition = nodes
                .iter()
                .map(|&n| n.title.as_str())
                .collect::<Vec<&str>>()
                .join("=");
            test_case_name.push_str(&condition);

            let step = format!("{}.{}", step_no, condition);
            test_case_steps.push(step);
            step_no += 1;
        } else {
            return None;
        }
    }

    let test_case = TestCase {
        project: project.title.to_string(),
        module: module.title.to_string(),
        function: function.title.to_string(),
        test_case_name,
        priority: "高".to_string(),
        test_case_type: "功能".to_string(),
        status: "使用".to_string(),
        steps: test_case_steps.join("\n"),
        expect: expect.to_string(),
    };

    Some(test_case)
}

pub fn write_xlsx(test_cases: Vec<TestCase>, mut path: std::path::PathBuf) -> std::io::Result<()> {
    if test_cases.is_empty() {
        println!("Warning: test case is empty");
        return Ok(());
    }

    let path = {
        let project = test_cases[0].project.as_str();
        let file_name = format!("{}_测试用例.xlsx", project);
        path.set_file_name(file_name);
        path
    };

    let mut file = {
        if path.exists() {
            std::fs::remove_file(&path)?;
        }
        std::fs::File::create(path)?
    };
    let mut workbook = WorkBook::new(&mut file)?;

    workbook.get_new_sheet().write_sheet(|writer| {
        writer.write_row(row![
            "文件夹",
            "编码",
            "测试用例类型*",
            "名称",
            "所属产品",
            "所属项目",
            "生命阶段*",
            "优先级",
            "编写人",
            "描述",
            "前置条件",
            "预期结果"
        ])?;

        for test_case in test_cases {
            writer.write_row(row![
                test_case.module,
                "",
                test_case.test_case_type,
                test_case.test_case_name,
                "",
                test_case.project,
                test_case.status,
                test_case.priority,
                "",
                test_case.steps,
                "",
                test_case.expect
            ])?;
        }

        Ok(())
    })?;

    workbook.finish()?;
    file.flush()?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_load_xmind() {
        let path = std::path::Path::new("/mnt/d/Desktop/模块-研发.xmind");
        match load_xmind(path) {
            Ok(xmind) => println!("{:?}", xmind),
            Err(e) => println!("{}", e),
        }
        assert!(true)
    }
}
